/** * @brief x lib is the library which includes the commonly used functions in 3 Sided Cube Android applications * * @author Callum Taylor **/ package in.lib.helper; import android.content.Context; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.Handler; import android.provider.Settings; import java.util.List; /** * @brief This class is used to fetch the user's current location using either * cached location (if available) or requests it using the * LocationListener if not */ public class LocationHelper implements LocationListener { /** * Message ID: Used when a provider has been disabled */ public static final int MESSAGE_PROVIDER_DISABLED = 0; /** * Message ID: Used when the search has timed out */ public static final int MESSAGE_TIMEOUT = 1; /** * Message ID: Used when the user cancels the request */ public static final int MESSAGE_FORCED_CANCEL = 2; private final Context mContext; private final LocationManager mLocationManager; private final long mTimeout = 0; private LocationResponse mCallback = null; private Accuracy mAccuracy = Accuracy.FINE; private final Handler mTimeoutHandler = new Handler(); private float mAccuracyFloat = 30.0f; private final Runnable mTimeoutRunnable = new Runnable() { @Override public void run() { mTimeoutHandler.removeCallbacks(mTimeoutRunnable); mLocationManager.removeUpdates(LocationHelper.this); stopFetch(); if (mCallback != null) { mCallback.onLocationFailed("Timeout", MESSAGE_TIMEOUT); mCallback.onTimeout(); } }; }; /** * Determines the accuracy of the fetch */ public enum Accuracy { /** * Get the location as close to the real point as possible */ FINE, /** * Get the location by any means */ COARSE; } /** * Default Constructor * * @param context * The application/activity context to use */ public LocationHelper(Context context) { mContext = context; mLocationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE); } /** * Cancels the request */ public void cancelRequest() { mTimeoutHandler.removeCallbacks(mTimeoutRunnable); mLocationManager.removeUpdates(this); if (mCallback != null) { mCallback.onLocationFailed("Canceled", MESSAGE_FORCED_CANCEL); } } /** * Sets the desired accuracy for the fetch * * @param accuracy * The new accuracy */ public void setAccuracy(float accuracy) { mAccuracyFloat = accuracy; } /** * Gets the current set desired accuracy for a fetch * * @return The accuracy in meters */ public float getAccuracy() { return mAccuracyFloat; } /** * Fetches the location using Fine accuracy. Note: if the response returns * location fetch failed, use the helper to get the cached location, then * finally fail if that is null * * @param timeout * The time out for the request in MS * @param callback * The callback for the request */ public void fetchLocation(long timeout, LocationResponse callback) { fetchLocation(timeout, Accuracy.FINE, callback); } /** * Fetches the location * * @param timeout * The time out for the request in MS * @param accuracy * The accuracy of the fetch * @param callback * The callback for the request */ public void fetchLocation(long timeout, Accuracy accuracy, LocationResponse callback) { mCallback = callback; mCallback.onRequest(); mAccuracy = accuracy; Location userLocation = null; // Try to get the cache location first userLocation = getCachedLocation(); // if (userLocation == null) { if (timeout > 0) { mTimeoutHandler.postDelayed(mTimeoutRunnable, timeout); } try { mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this); } catch (Exception e) { e.printStackTrace(); } try { mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this); } catch (Exception e) { e.printStackTrace(); } try { mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this); } catch (Exception e) { e.printStackTrace(); } } } /** * Gets the cached location * * @return The location, or null if one was not retrieved */ public Location getCachedLocation() { List<String> providers = mLocationManager.getProviders(true); Location l = null; for (int i = providers.size() - 1; i >= 0; i--) { Location loc = mLocationManager.getLastKnownLocation(providers.get(i)); if (l == null || (loc != null && loc.getAccuracy() < l.getAccuracy())) { l = loc; } } return l; } private boolean hasAcquired = false; @Override public void onLocationChanged(Location location) { if (location != null) { if (mCallback != null) { mCallback.onLocationChanged(location); if (mAccuracy == Accuracy.FINE && (!location.hasAccuracy() || location.getAccuracy() > mAccuracyFloat)) { return; } if (!hasAcquired) { mTimeoutHandler.removeCallbacks(mTimeoutRunnable); mLocationManager.removeUpdates(this); mCallback.onLocationAcquired(location); hasAcquired = true; } } } } public void stopFetch() { mTimeoutHandler.removeCallbacks(mTimeoutRunnable); mLocationManager.removeUpdates(this); } @Override public void onProviderDisabled(String provider) { List<String> providers = mLocationManager.getProviders(true); boolean allOn = false; for (int i = providers.size() - 1; i >= 0; i--) { allOn |= Settings.Secure.isLocationProviderEnabled(mContext.getContentResolver(), providers.get(i)); } if (!allOn) { mTimeoutHandler.removeCallbacks(mTimeoutRunnable); mLocationManager.removeUpdates(this); if (mCallback != null) { mCallback.onLocationFailed("All providers disabled", MESSAGE_PROVIDER_DISABLED); } } } @Override public void onProviderEnabled(String provider) { } @Override public void onStatusChanged(String provider, int status, Bundle extras) { } /** * @brief The location response for the callback of the LocationHelper */ public static abstract class LocationResponse { /** * Called when the request was initiated */ public void onRequest() { } /** * Called when the location changes * * @param l * The new location */ public void onLocationChanged(Location l) { } /** * Called when the location was acquired * * @param l * The location received */ public abstract void onLocationAcquired(Location l); /** * Called when the request timed out */ public void onTimeout() { } /** * Called when the request failed * * @param message * The message * @param messageId * The ID of the message */ public void onLocationFailed(String message, int messageId) { } } }